 /**
 * 文件名称: framebuffer_png.c
 * 摘    要: linux framebuffer操作png图片源文件
 * 来    源: 参考朱有鹏老师嵌入式linux核心课程中《图片解码播放器》编写的图片播放器工程
 *
 * 当前版本: 1.0
 * 作    者: huenrong
 * 完成日期: 2019-07-20
 **/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>


#include "../framebuffer/framebuffer.h"
#include "framebuffer_png.h"
#include "png.h"
// 下面两个头文件在libpng源码根目录下(不是交叉编译后生成的)
#include "pngstruct.h"      // 这个一定要放在png.h的后面包含
#include "pnginfo.h"

#define PNG_BYTES_TO_CHECK      8

/******************************************************************************
函数名称: judge_png_picture
函数功能: 判断一个图片是否是png格式图片
函数参数: path_name: 图片路径
函数返回: 0: 图片是png格式图片
          -1: 图片不是png格式图片
******************************************************************************/
int judge_png_picture(const char *path_name)
{
    FILE *fd = NULL;
    unsigned char buf[PNG_BYTES_TO_CHECK] = {0};

    fd = fopen(path_name, "rb");        // 打开文件

    /* Open the prospective PNG file. */
    if (fd == NULL)
    {
        return -1;
    }

    /* Read in some of the signature bytes */
    if (PNG_BYTES_TO_CHECK != fread(buf, 1, PNG_BYTES_TO_CHECK, fd))
    {
        return -1;
    }

    fclose(fd);

    /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.
    Return nonzero (true) if they match */
    return(png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK));
}

/******************************************************************************
函数名称: analysis_png_picture
函数功能: 解析png图片
函数参数: path_name: 图片路径
函数返回: 成功: 返回0
          失败: 返回-1
******************************************************************************/
static int analysis_png_picture(const char *path_name, struct picture_info *picture)
{
    FILE *fp = NULL;
    png_structp png_ptr = {0};
    png_infop info_ptr = {0};
    int color_type = -1;
    png_bytep *row_pointers;
    unsigned long length = 0;       // 像素点数据长度
    int position = 0;       // 像素点数据在图像数组中的位置(一个像素点3个位置, 分别是RGB)
    int x = 0;      // 屏幕宽度索引
    int y = 0;      // 屏幕高度索引

    fp = fopen(path_name, "rb");
    if (NULL == fp)
    {
        perror("analysis_png_picture fopen error");
        return -1;
    }

    // 相关结构体初始化
    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
    if (0 == png_ptr)
    {
        fclose(fp);

        return -1;
    }

    info_ptr = png_create_info_struct(png_ptr);
    if (0 == info_ptr)
    {
        png_destroy_read_struct(&png_ptr, 0, 0);
        fclose(fp);

        return -1;
    }

    // 设置错误处理函数
    if (setjmp(png_jmpbuf(png_ptr)))
    {
        png_destroy_read_struct(&png_ptr, &info_ptr, 0);
        fclose(fp);

        return -1;
    }

    // 将要解码的png图片的文件指针和png解码器绑定起来
    png_init_io(png_ptr, fp);

    // 读取png图片信息
    png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_ALPHA, 0);

    // 相关图片信息打印出来看一看
    color_type = info_ptr->color_type;

    picture->width = info_ptr->width;
    picture->height = info_ptr->height;
    picture->bpp = info_ptr->pixel_depth;

    printf("------------------png picutre info------------------\r\n");
    printf("picutre resolution: %d x %d.\n", picture->width, picture->height);
    printf("picture bpp: %d.\n", picture->bpp);
    printf("color_type = %d\n", color_type);
    printf("----------------------------------------------------\r\n");

    // 读取真正的图像信息
    row_pointers = png_get_rows(png_ptr, info_ptr);

    length = info_ptr->width * info_ptr->height * 3;        // 计算像素点数据长度(1个像素点占3字节)

    // 分配length块长度为1字节的连续区域, 用于存放整个图像数据
    picture->data = (char *)calloc(1, length);

    // 图像数据移动到我们自己的buf中(只处理RGB24位真彩色图片)
    if (PNG_COLOR_TYPE_RGB == color_type)
    {
        for(y=0; y<picture->height; y++)
        {
            for(x=0; x<3*picture->width; x+=3)
            {
                picture->data[position++] = row_pointers[y][x + 0];
                picture->data[position++] = row_pointers[y][x + 1];
                picture->data[position++] = row_pointers[y][x + 2];
            }
        }
    }

    // 收尾处理
    png_destroy_read_struct(&png_ptr, &info_ptr, 0);

    fclose(fp);     // 关闭文件

    return 0;
}

/******************************************************************************
函数名称: display_png_picture
函数功能:显示png图片
函数参数: path_name: 图片路径
          x_start: 图片起点坐标x值
          y_start: 图片起点坐标y值
函数返回: 成功: 返回0
          失败: 返回-1
******************************************************************************/
int display_png_picture(const char *path_name, const unsigned int x_start, const unsigned int y_start)
{
    int ret = -1;
    struct picture_info picture = {0};

    ret = judge_png_picture(path_name);        // 判断是否是png图片
    if (0 != ret)
    {
        return -1;
    }

    ret = analysis_png_picture(path_name, &picture);        // 解析png图片
    if (0 != ret)
    {
        return -1;
    }

    framebuffer_display_picture(x_start, y_start, 2, &picture);      // 指定显示png格式的图片

    free(picture.data);     // 显示完成, 释放存放整个图像数据的内存空间

    return 0;
}
